//
//  except.c
//
//
//  Created by 周凯 on 15/8/28.
//
//
#include "except.h"
#include "exception.h"

#ifndef ERROR_MESSAGE_MAX_SIZE
#define       ERROR_MESSAGE_MAX_SIZE (512)
#endif

THREAD_LOCAL ExceptFrameT   *_g_ef_ = NULL;
THREAD_LOCAL volatile int   _g_en_ = 0;
static THREAD_LOCAL char 	_g_em_[ERROR_MESSAGE_MAX_SIZE] = { 0 };

#ifdef __WINDOWS__
	#define strerror_r(e, p, s) 	strerror_s((p), (s), (e))
	#define snprintf(b, s, f, ...) 	_snprintf_s((b), (s), (unsigned) - 1, (f), ##__VA_ARGS__)
#endif

#define SWITCH_NULL_STR(s) (unlikely((void *)(s) == (void *)0) ? "NULL" : (s))
#define NEG(x) ((x) > 0 ? -(x) : (x))

/**输出日志*/
#ifndef HAVE_COLOR_LOG
  #define LOG_W_COLOR ""
  #define LOG_F_COLOR ""
  #define COLOR_NONE  ""
  #define COLOR_WHITE ""
#else
  #define LOG_W_COLOR "\x1B[1;33m"
  #define LOG_F_COLOR "\x1B[1;31m"
  #define COLOR_NONE  "\x1B[m"
  #define COLOR_WHITE "\x1B[1;37m"
#endif

#define LOG_W_LEVEL	"WARN "
#define LOG_F_LEVEL	"FATAL"

#if GCC_VERSION
  #define BaseName(n)	({ const char *p = strrchr((n), '/'); p ? p + 1 : (n); })
#else
  #define BaseName(n)	(strrchr((n), '\\') ? strrchr((n), '\\') + 1 : (n))
#endif

#if GCC_VERSION
  #define SLogWriteByInfo(level, func, file, line, fmt, ...)							    \
  fprintf(stdout,									    \
	"[" LOG_##level##_COLOR LOG_##level##_LEVEL	    \
	COLOR_WHITE "]|%32s:%04d|%32s()" LOG_##level##_COLOR "|" fmt "\n" COLOR_NONE, \
	BaseName(file), (line), (func), ##__VA_ARGS__)
#else
  #define SLogWriteByInfo(level, func, file, line, fmt, ...)			       \
  STMT_BEGIN						       \
  fprintf(stdout, \
	"[%s]|%32s:%04d|%32s()|", LOG_##level##_LEVEL, \
	BaseName(file), (line), (func)); \
  fprintf(stdout, fmt, ##__VA_ARGS__);	       \
  fprintf(stdout, "\n");			       \
  STMT_END
#endif

/**windows错误POSIX定义*/
#ifdef __WINDOWS__
  #define E(code, s) { code, (s " [" #code " ]") }
  static const struct errorTable
  {
	int             code;
	const char      *msg;
  } socketStrErrorTable[] = {
	E(WSAEINTR,           "Interrupted function call"),
	E(WSAEACCES,          "Permission denied"),
	E(WSAEFAULT,          "Bad address"),
	E(WSAEINVAL,          "Invalid argument"),
	E(WSAEMFILE,          "Too many open files"),
	E(WSAEWOULDBLOCK,     "Resource temporarily unavailable"),
	E(WSAEINPROGRESS,     "Operation now in progress"),
	E(WSAEALREADY,        "Operation already in progress"),
	E(WSAENOTSOCK,        "Socket operation on nonsocket"),
	E(WSAEDESTADDRREQ,    "Destination address required"),
	E(WSAEMSGSIZE,        "Message too long"),
	E(WSAEPROTOTYPE,      "Protocol wrong for socket"),
	E(WSAENOPROTOOPT,     "Bad protocol option"),
	E(WSAEPROTONOSUPPORT, "Protocol not supported"),
	E(WSAESOCKTNOSUPPORT, "Socket type not supported"),
	/* What's the difference between NOTSUPP and NOSUPPORT? :) */
	E(WSAEOPNOTSUPP,      "Operation not supported"),
	E(WSAEPFNOSUPPORT,    "Protocol family not supported"),
	E(WSAEAFNOSUPPORT,    "Address family not supported by protocol family"),
	E(WSAEADDRINUSE,      "Address already in use"),
	E(WSAEADDRNOTAVAIL,   "Cannot assign requested address"),
	E(WSAENETDOWN,        "Network is down"),
	E(WSAENETUNREACH,     "Network is unreachable"),
	E(WSAENETRESET,       "Network dropped connection on reset"),
	E(WSAECONNABORTED,    "Software caused connection abort"),
	E(WSAECONNRESET,      "Connection reset by peer"),
	E(WSAENOBUFS,         "No buffer space available"),
	E(WSAEISCONN,         "Socket is already connected"),
	E(WSAENOTCONN,        "Socket is not connected"),
	E(WSAESHUTDOWN,       "Cannot send after socket shutdown"),
	E(WSAETIMEDOUT,       "Connection timed out"),
	E(WSAECONNREFUSED,    "Connection refused"),
	E(WSAEHOSTDOWN,       "Host is down"),
	E(WSAEHOSTUNREACH,    "No route to host"),
	E(WSAEPROCLIM,        "Too many processes"),

	/* Yes, some of these start with WSA, not WSAE. No, I don't know why. */
	E(WSASYSNOTREADY,     "Network subsystem is unavailable"),
	E(WSAVERNOTSUPPORTED, "Winsock.dll out of range"),
	E(WSANOTINITIALISED,  "Successful WSAStartup not yet performed"),
	E(WSAEDISCON,         "Graceful shutdown now in progress"),
  #ifdef WSATYPE_NOT_FOUND
	E(WSATYPE_NOT_FOUND,  "Class type not found"),
  #endif
	E(WSAHOST_NOT_FOUND,  "Host not found"),
	E(WSATRY_AGAIN,       "Nonauthoritative host not found"),
	E(WSANO_RECOVERY,     "This is a nonrecoverable error"),
	E(WSANO_DATA,         "Valid name, no data record of requested type"),

	{ -1,                 NULL },
};
  #undef E

const char *socketStrError(int error)
{
	/* XXXX Is there really no built-in function to do this? */
	for (int i = 0; socketStrErrorTable[i].code >= 0; ++i)
		if (error == socketStrErrorTable[i].code)
			return socketStrErrorTable[i].msg;

	return NULL;
}
#endif // ifdef __WINDOWS__


/**处理getaddrinfo()的错误*/
static const char *GaiStrError(int error)
{
	switch (error)
	{
#ifdef EAI_ADDRFAMILY
		case NEG(EAI_ADDRFAMILY):
		{ return "address family for host not supported"; }
#endif
#ifdef EAI_AGAIN
		case NEG(EAI_AGAIN):
		{ return "temporary failure in name resolution"; }
#endif
#ifdef EAI_BADFLAGS
		case NEG(EAI_BADFLAGS):
		{ return "invalid flags value"; }
#endif
#ifdef EAI_FAIL
		case NEG(EAI_FAIL):
		{ return "non-recoverable failure in name resolution"; }
#endif
#ifdef EAI_FAMILY
		case NEG(EAI_FAMILY):
		{ return "address family not supported"; }
#endif
#ifdef EAI_MEMORY
		case NEG(EAI_MEMORY):
		{ return "memory allocation failure"; }
#endif
#ifdef EAI_NODATA
		case NEG(EAI_NODATA):
		{ return "no address associated with host"; }
#endif
#if defined(EAI_NONAME) && (EAI_NODATA != EAI_NONAME)
		case NEG(EAI_NONAME):
		{ return "host nor service provided, or not known"; }
#endif
#ifdef EAI_SERVICE
		case NEG(EAI_SERVICE):
		{ return "service not supported for socket type"; }
#endif
#ifdef EAI_SOCKTYPE
		case NEG(EAI_SOCKTYPE):
		{ return "socket type not supported"; }
#endif
#ifdef EAI_SYSTEM
		case NEG(EAI_SYSTEM):
		{ return "system error"; }
#endif
		default:
		{ return "unkown error"; }
	}
}

static const char * StrError(int error)
{
	/**不能调用有日志相关函数*/
	char *ptr = &_g_em_[0];
	
	ptr[0] = '\0';

	if (unlikely(error < 0)) {
		snprintf(ptr, sizeof(_g_em_), "%s", GaiStrError(error));
	} else {
			
#if defined(__LINUX__) && ((_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600) || _GNU_SOURCE)
		char *rc = NULL;
		rc = strerror_r(error, ptr, sizeof(_g_em_));

		if (unlikely(rc == NULL))
#else
		int rc = 0;
  #ifdef __WINDOWS__
  		const char *p = socketStrError(error);
  		if (ptr) {
  			rc = snprintf(ptr, sizeof(_g_em_), "%s", ptr);
  		} else
  #endif
		{
			rc = strerror_r(error, ptr, sizeof(_g_em_));
		}

		if (unlikely(rc < 0)) 
#endif /* if defined(__LINUX__) */
		{
			snprintf(ptr, sizeof(_g_em_),
				"Unkown error [%d], or call strerror_r() failed", error);
		}
	}

	return ptr;
}


void ExceptRaise(unsigned skip, const char *func, const char *file, int line, const ExceptT *e)
{
	ExceptFrameT *ptr = NULL;
	ExceptFrameT *prevptr = NULL;
	
	if (likely(e == &EXCEPT_SYS)) {
		/**保存错误值*/
		if (_g_en_ == 0) {
#ifndef __WINDOWS__
			_g_en_ = errno;
#else
			/*优先尝试过去套接字的错误信息*/
			int error = WSAGetLastError();
			_g_en_ = (error != 0 ? error : errno);
#endif
		}
	}
	/*
	 * 从栈中弹出当前抛出的异常
	 */
	ptr = PopExceptFrame();

	if (unlikely((!ptr) || (!e))) {
		SLogWriteByInfo(F, func, file, line,
			"Uncaught exception [ 0x%08x : %s ].\n"
			"aborting ...",
			likely(e) ? e->exceptNo : EXCEPT_ASSERT.exceptNo,
			likely(e) ? (e == &EXCEPT_SYS ? StrError(_g_en_) :
				SWITCH_NULL_STR(e->exceptName)) : EXCEPT_ASSERT.exceptName);

		abort();
	}

	/**是否为skip-try*/
	if (unlikely(skip)) {
		/**找到上一个被标记的skip-try块，该异常还会向上skip-raise，直到找到正确的skip-try*/
		while (likely(ptr && !ptr->skipable)) {
			prevptr = ptr;
			ptr = PopExceptFrame();
			if (likely(ptr)) ptr->layer = prevptr->layer;
		}
		/**防止最顶层被弹出*/
		if (unlikely(ptr == NULL)) ptr = prevptr;
		/**如果标记的skip-try块的异常没有catch，那么继续skip-raise*/
		ptr->skipable = 1;
	}

	/**打印异常跟踪*/
	if (likely(e == &EXCEPT_SYS)) {
		SLogWriteByInfo(W, func, file, line,
			"%03d. %s : [%d : %s].",
			ptr->layer,
			SWITCH_NULL_STR(e->exceptName),
			_g_en_,
			StrError(_g_en_));
	} else {
		SLogWriteByInfo(W, func, file, line,
			"%03d. [ 0x%08x : %s].",
			ptr->layer,
			e->exceptNo,
			SWITCH_NULL_STR(e->exceptName));
	}

	/**为捕捉比较异常，再次抛出做准备*/
	ptr->except = e;

	/*
	 * 跳转到最近的TRY-CATCH-FINALLY模块的处理（或结束）处
	 */
	siglongjmp(ptr->env, _ET_RAISED);
}

